import fs from 'node:fs'
import path from 'node:path'
// import { loadConfig } from 'unconfig'
import { parseConfig } from '../parse'
import { isDirectory } from './utils'

/**
 * 获取除了.env文件外所有env文件的内容
 * @param envDir env文件所在目录
 * @returns {{dev:{},test: {}}} 返回环境变量map
 */
export async function readAndParseEnv(envDir: string, envType: 'env' | 'ts') {
  const devMap: Record<string, any> = {}
  const comaMap: Record<string, any> = {}

  const devNames = getFiles(envDir, envType)
  for (let i = 0; i < devNames.length; i++) {
    const fileName = devNames[i]
    await createEnvObj({ envType, envDir, fileName }, devMap, comaMap)
  }
  const tempCommonEnv = devMap.common || {}
  const tempCommonEnvComa = comaMap.common || {}

  Object.keys(devMap).forEach(key => {
    if (key !== 'common') {
      devMap[key] = Object.assign({}, devMap[key], tempCommonEnv)
      comaMap[key] = Object.assign({}, comaMap[key], tempCommonEnvComa)
    }
  })

  return { devMap, comaMap }
}
async function createEnvObj({ envType, envDir, fileName }: { envType: string, envDir: string, fileName: string }, devMap: Record<string, any>, comaMap: Record<string, any>) {
  try {
    if (envType === 'env') {
      const fileStr = fs.readFileSync(path.join(envDir, fileName), 'utf8')
      if (fileName === '.env') {
        devMap.common = envVarType(parseEnvToObj(fileStr))
      }
      else {
        // 排除公共的.dev文件
        const envMode = fileName.replace('.env.', '')
        devMap[envMode] = envVarType(parseEnvToObj(fileStr))
      }
    }
    else {
      // 正则去除文件任意后缀名
      const envPath = path.join(envDir, fileName)
      const fileStr = fs.readFileSync(envPath, 'utf8')
      
      const configList = await parseConfig({ sources: [{ files: envPath }] }) || {}
      const config = configList[0]?.config || {}
      
      fileName = fileName.replace(/\.[^.]*$/, '') // 去除后缀后的文件名
      if (fileName === '.env') {
        devMap.common = config || {}
        comaMap.common = parseTsComa(fileStr)
      }
      else {
        // 去除.env和.ts其它作为环境名
        const envMode = fileName.replace('.env.', '')
        devMap[envMode] = config || {}
        comaMap[envMode] = parseTsComa(fileStr)
      }
    }
  }
  catch (e) {
    console.log(e)
  }
}

// 获取当前目录下所有文件及其路径
function getFiles(dirPath: string, envType: 'env' | 'ts') {
  //   // 匹配以.env开头并且.ts结尾的正则
  // const tsFileReg = new RegExp('^.env.+?ts$')// /^.env\..*?ts$/
  // // 匹配以.env开头并且不是.ts结尾的正则
  // const envFileReg = /^\.env(?!.*\.ts$).*/
  const files: string[] = []
  try {
    const items: string[] = fs.readdirSync(dirPath)
    for (let i = 0; i < items.length; i++) {
      const itemPath = path.join(dirPath, items[i])

      if (!isDirectory(itemPath)) {
        if (envType === 'env' && items[i].startsWith('.env')) {
          files.push(items[i])
        }
        else {
          // 判断文件后缀名是否符合要求（这里以".ts"为例）
          const match = items[i].match(/\.[^.]*$/)
          if (match && match[0] === `.${envType}`) {
            files.push(items[i])
          }
        }
      }
    }
  }
  catch (e) {
    console.log(e)
  }
  return files
}

function envVarType(envConf = {} as any): object {
  const ret: { [x: string]: any } = {}
  for (const envName of Object.keys(envConf)) {
    let realName = envConf[envName] || ''
    // realName = realName.replace(/\\n/g, '\n')
    realName = realName === 'true' ? true : realName === 'false' ? false : realName
    if (['VITE_PORT', 'VITE_APP_TIME_OUT'].includes(envName)) realName = Number(realName)
    ret[envName] = realName
  }
  return ret
}

const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm

// Parse src into an Object
export function parseEnvToObj(src: string) {
  const obj: Record<string, any> = {}
  // Convert buffer to string
  let lines = src.toString()
  // Convert line breaks to same format
  lines = lines.replace(/\r\n?/g, '\n')
  let match
  while ((match = LINE.exec(lines)) !== null) {
    const key = match[1]
    // Default undefined or null to empty string
    let value = (match[2] || '')
    // Remove whitespace
    value = value.trim()
    // Check if double quoted
    const maybeQuote = value[0]
    // Remove surrounding quotes
    value = value.replace(/^(['"`])([\s\S]*)\1$/gm, '$2')
    // Expand newlines if double quoted
    if (maybeQuote === '"') {
      value = value.replace(/\\n/g, '\n')
      value = value.replace(/\\r/g, '\r')
    }
    // Add to object
    obj[key] = value
  }
  return obj
}

function parseTsComa(lines: string) {
  const keyValueRegex = /(\w+)\s*:\s*(?:(['"])(.*?)\2|\b(?:true|false|\d+(?:\.\d+)?)\b)/g

  // 用于移除单行注释 // ...
  const singleLineCommentRegex = /([^\\:]|^)\/\/(.*)$/gm
  const env: Record<string, any> = {}
  lines = lines.replace(/\r\n?/g, '\n')
  const lineArr = lines.split('\n')
  const temp: Record<string, any>[] = []

  lineArr.forEach((line: string) => {
    // 去除行末的空白字符
    line = line.trim()

    // 如果行为空，则跳过
    if (!line) return
    const isKeyValue = keyValueRegex.test(line)
    if (isKeyValue) { // 合法的key value
      line.replace(keyValueRegex, (_match: any, key: any, _quote: any, value: any) => {
        const rawVal = value
        const val = line.replace(value, 'PLACEHOLDER')
        // 移除多行注释
        val.replace(/\/\*([\s\S]*?)\*\//g, (_match: any, value: any) => {
          temp.push({ comment: true, line: value })
          return ''
        })
        // 移除单行注释
        val.replace(singleLineCommentRegex, (_match: any, _key: any, value: any) => {
          temp.push({ comment: true, line: value })
          return ''
        })
        temp.push({ comment: false, line: { [key]: rawVal } })
        return ''
      })
    }
    else {
      // 不合法的key value，比如 key: 11false，或者注释会走该分支
      // 移除多行注释
      line.replace(/\/\*([\s\S]*?)\*\//g, (_match, value: string) => {
        temp.push({ comment: true, line: value })
        return ''
      })
      // 移除单行注释
      line.replace(singleLineCommentRegex, (_match: any, _key: any, value: string) => {
        temp.push({ comment: true, line: value })
        return ''
      })
    }
  })
  let temp2: Record<string, any>[] = []
  temp.forEach(item => {
    if (item.comment) {
      temp2.push(item.line)
    }
    else {
      env[Object.keys(item.line)[0]] = [...temp2]
      temp2 = []
    }
  })
  return env
}
